#include <QDir>
#include <QNetworkReply>

// from WccApi
#include "wccapi/wccbuild.h"
#include "wccapi/wccdownloadbinaryrequest.h"
#include "wccapi/wccnetworkaccessmanager.h"
#include "wccapi/wccnetworkrequestbase.h"
#include "wccapi/wccpostfilerequest.h"

#include "generatoroutputview.h"
#include "rcbuildtask.h"
#include "rcsession.h"
#include "common/fstraverseiterator.h"

namespace
{
    const int UPLOAD_TIMEOUT_MSEC                   = 30000;

    // 60 min (server should handle the timeout)
    const int BUILD_TIMEOUT_MSEC                    = 3600000;
}

RcBuildTask::RcBuildTask(GeneratorOutputView       & generatorOutput,
                         RcSession                 & session,
                         const QString             & projectName,
                         const QString             & projectDir,
                         QObject                   * parent)
    : QObject(parent)
    , m_generatorOutput(generatorOutput)
    , m_session(session)
    , m_projectName(projectName)
    , m_projectDir(projectDir)
    , m_build(NULL)
{
    traverseProjectDir();
}

#include <stdlib.h>

void RcBuildTask::startBuilding(const QString & wccParams,
                                const QString & downloadDir)
{
    m_wccParams = wccParams;
    m_downloadDir = downloadDir;

    emit zipping();

    QString
        msg = QString("Starting build process for %1 in %2 for %3").arg(m_projectName,
                                                                        m_projectDir,
                                                                        m_wccParams);

    m_generatorOutput.printOutput(msg);
    QString
        requestUrl = RcSession::makeRequestUrl(QString("build?") + wccParams);

    m_build = new WccApi::WccBuild(requestUrl);

    connect(m_build,
            SIGNAL(processFinished(QString,QString)),
            this,
            SLOT(zipProcessSucceedSlot(QString,QString)));
    connect(m_build,
            SIGNAL(processFailed(QString)),
            this,
            SLOT(zipProcessFailedSlot(QString)));

    m_generatorOutput.printOutput("  - project dir: " + m_projectDir);
    m_generatorOutput.printOutput("  - project name: " + m_projectName);

    m_generatorOutput.printOutput("  - zipping ...");

    QDir zipTargetDir(m_projectDir);
    zipTargetDir.mkdir("foobar");

    //zipTargetDir.cdUp();

    foreach (QString fileName, m_projectFiles) {
        m_generatorOutput.printOutput("  - zipping file: " + fileName);
    }

    m_savedPath = QDir::currentPath();
    QDir::setCurrent(zipTargetDir.absolutePath());

    m_build->startProcess(zipTargetDir.absolutePath(),
                          m_projectName,
                          m_projectFiles);
}


void RcBuildTask::zipProcessSucceedSlot(const QString & buildUrl,
                                        const QString & zipFileName)
{
    QDir::setCurrent(m_savedPath);
    emit uploadingAndWaiting();

    int
        id = m_session.nextReqId();
    WccApi::WccNetworkAccessManager
        * netAccessManager = m_session.networkAccessManager();

    m_generatorOutput.printOutput(QString("  - zipping succeeded: %1").arg(zipFileName));

    m_generatorOutput.printOutput("  - uploading ...");

    netAccessManager->sendZipToRemoteCompiler(id,
                                              buildUrl,
                                              zipFileName);

    WccApi::WccPostFileRequest
        * req = qobject_cast<WccApi::WccPostFileRequest*> (netAccessManager->requestById(id));

    if (!req)
    {
        m_generatorOutput.printOutput("Could not get post request");
        emit buildCompleted(false,
                            "");
        return;
    }

    QNetworkReply
        * reply = req->reply();

    connect(reply,
            SIGNAL(uploadProgress(qint64,qint64)),
            this,
            SLOT(uploadedSlot(qint64,qint64)));

    connect(req,
            SIGNAL(finished()),
            this,
            SLOT(sendZipOkSlot()));

    m_timeout.setSingleShot(true);
    connect(&m_timeout,
            SIGNAL(timeout()),
            this,
            SLOT(operationTimedOutSlot()));
    m_timeout.start(UPLOAD_TIMEOUT_MSEC);
}


void RcBuildTask::zipProcessFailedSlot(const QString & eMsg)
{
    QDir::setCurrent(m_savedPath);
    m_generatorOutput.printOutput("zipping failed.");
    emit buildCompleted(false,
                        "");
}


void RcBuildTask::sendZipOkSlot()
{
    emit downloading();

    m_generatorOutput.printOutput("waiting for remote build");

    m_timeout.stop();
    int
        id = m_session.nextReqId();

    QObject
        *oreq = sender();

    WccApi::WccPostFileRequest
        * req = qobject_cast<WccApi::WccPostFileRequest*>(oreq);

    if (!req)
    {
        m_generatorOutput.printOutput("Could not get post request");
        emit buildCompleted(false,
                            "");
        return;
    }

    WccApi::WccResponse
        *resp = req->response();
    QString
        msg = resp->message();
    QString
        prj,
        log,
        status;
    WccApi::WccNetworkAccessManager
        * netAccessManager = m_session.networkAccessManager();
    netAccessManager->parsePostFileReply(msg,
                                         prj,
                                         log,
                                         status);
    parseBuildLog(log);

    m_generatorOutput.printOutput(log);


    m_generatorOutput.printOutput(QString("build status: \"%1\"").arg(status));

    if (status == "fail") {
        m_generatorOutput.printOutput("build failed.");
        emit buildCompleted(false,
                            "");
        return;
    }

    //NOTE: download doesn't yet support pSet arguments
    QString
        request = m_wccParams;
    request = request.replace("pSet[os]", "os");
    request = request.replace("pSet[qt]", "qt");
    request = QString("download?installer=1&%1&project=%2").arg(request, prj);
    QString
        url = RcSession::makeRequestUrl(request);
    QString
        dlpath = m_downloadDir;
    netAccessManager->downloadBinary(id,
                                     url,
                                     dlpath);
    WccApi::WccNetworkRequestBase
        *b = netAccessManager->requestById(id);
    connect(b,
            SIGNAL(finished()),
            this,
            SLOT(downloadFinishedSlot()));
}


void RcBuildTask::uploadedSlot(qint64 sent,
                               qint64 total)
{
    QString upl = QString("Uploaded: %1/%2 kb").arg(sent/1024).arg(total/1024);
    m_generatorOutput.printOutput(upl);

    if (total == sent) {
        m_timeout.start(BUILD_TIMEOUT_MSEC);
        m_generatorOutput.printOutput("  - waiting for remote build to complete");
    }
}


void RcBuildTask::operationTimedOutSlot()
{
    m_generatorOutput.printOutput("Operation timed out.");
    emit buildCompleted(false,
                        "");
}


void RcBuildTask::downloadFinishedSlot()
{
    QObject
        *oreq = sender();
    WccApi::WccDownloadBinaryRequest
        * req = qobject_cast<WccApi::WccDownloadBinaryRequest*>(oreq);

    if (!req)
    {
        m_generatorOutput.printOutput("Could not get download request");
        emit buildCompleted(false,
                            "");
        return;
    }

    QString
        file = req->response()->message();
    file.remove('"');
    QString msg = QString("Binary downloaded to: ") + file;
    m_generatorOutput.printOutput(msg);
    emit buildCompleted(true,
                        file);
}


void RcBuildTask::traverseProjectDir()
{
    FSTraverseIterator
        it(m_projectDir,
           FSTraverseIterator::Files),
        end;

    QDir projectDir(m_projectDir);

    m_generatorOutput.printOutput("  - Creating source directory for zip");

    for (; it != end; ++it)
        m_projectFiles << projectDir.relativeFilePath(it->filePath());
}


void RcBuildTask::parseBuildLog(const QString & fullLog)
{
    ; // TODO - necessary? probably not
}

